//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================
#include "ddk.h"

#include "FileOps.h"
#include <stdio.h>

/*
 * The number of seconds between Jan 1, 1970 and Jan 1, 1980. In that
 * interval there were 8 regular years and 2 leap years.
 */
#define    SECONDSTO1980    (((8 * 365) + (2 * 366)) * (24 * 60 * 60))

ECode FGetType(
    struct node *pNode,
    /* [out] */ FileType *pType)
{
    if (NULL == pType) {
        return E_INVALID_ARGUMENT;
    }

    switch (pNode->n_type) {
        case T_DIR:
            *pType = FileType_Directory;
            break;

        case T_FILE:
            *pType = FileType_RegularFile;
            break;

        case T_SYM:
            *pType = FileType_RegularFile;
            break;

        default:
            DPRINTF(("ERROR:Unknown file type = 0x%08x!\n", pNode->n_type));
            return E_INVALID_ARGUMENT;
    }

    return NOERROR;
}

ECode FGetAccessTime(
    class CFatFile *pFile,
    /* [out] */ Int32 * pTime)
{
    FILESTAT Stat;
    UInt32 grfStatFlag = 0;

    if (NULL == pTime) {
        return E_INVALID_ARGUMENT;
    }

    pFile->pFatDir->fatfs_stat(pFile, &Stat, &grfStatFlag);
    *pTime = Stat.atime;
    return NOERROR;
}

ECode FSetAccessTime(
    class CFatFile *pFile,
    /* [in] */ Int32 time)
{
/* time to set must after 1980.1.1 */
    if (time < SECONDSTO1980) {
        return E_INVALID_ARGUMENT;
    }

    if (pFile->f_node->n_sharemode != FileShareMode_Write &&
        (UInt32)pFile != pFile->f_node->n_firsthandle) {
        return E_FS_FILE_BUSY;
    }

    if (!(pFile->f_node->n_mode & ACC_WRITE)) {
        return ErrorToECODE(EPERM, "FSetAccessTime");
    }

    pFile->pFatDir->dir_timeaccess(pFile->f_node, time);
    pFile->pFatDir->sync();
    return NOERROR;
}

ECode FGetModificationTime(
    class CFatFile *pFile,
    /* [out] */ Int32 * pTime)
{
    FILESTAT Stat;
    UInt32 grfStatFlag = 0;

    if (NULL == pTime) {
        return E_INVALID_ARGUMENT;
    }

    pFile->pFatDir->fatfs_stat(pFile, &Stat, &grfStatFlag);
    *pTime = Stat.mtime;
    return NOERROR;
}

ECode FSetModificationTime(
    class CFatFile *pFile,
    /* [in] */ Int32 time)
{
/* time to set must after 1980.1.1 */
    if (time < SECONDSTO1980) {
        return E_INVALID_ARGUMENT;
    }

    if (pFile->f_node->n_sharemode != FileShareMode_Write &&
        (UInt32)pFile != pFile->f_node->n_firsthandle) {
        return E_FS_FILE_BUSY;
    }

    if (!(pFile->f_node->n_mode & ACC_WRITE)) {
        return ErrorToECODE(EPERM, "FSetModificationTime");
    }

    pFile->pFatDir->dir_timestamp(pFile->f_node, time);
    pFile->pFatDir->sync();
    return NOERROR;
}

ECode FGetCreateTime(
    class CFatFile *pFile,
    /* [out] */ Int32 * pTime)
{
    FILESTAT Stat;
    UInt32 grfStatFlag = 0;

    if (NULL == pTime) {
        return E_INVALID_ARGUMENT;
    }

    pFile->pFatDir->fatfs_stat(pFile, &Stat, &grfStatFlag);
    *pTime = Stat.ctime;
    return NOERROR;
}

ECode FCheckAccessMode(
    class CFatFile *pFile,
    /* [in] */ FileAccessMode mode)
{
    UInt32 nmode = pFile->f_node->n_mode;

    switch (mode) {
        case FileAccessMode_Read:
            if (nmode & ACC_READ) {
                return NOERROR;
            }
            break;

        case FileAccessMode_Write:
            if (nmode & ACC_WRITE) {
                return NOERROR;
            }
            break;

        case FileAccessMode_ReadWrite:
            if ((nmode & ACC_READ) && (nmode & ACC_WRITE)) {
                return NOERROR;
            }
            break;

        default:
            DPRINTF(("Unknown file mode = 0x%08x!\n", mode));
            return E_INVALID_ARGUMENT;
    }
    return E_FS_NO_PERMISSION;
}

ECode FGetAccessMode(
    class CFatFile *pFile,
    /* [out] */ FileAccessMode * pMode)
{
    UInt32 mode = pFile->f_node->n_mode;

    if (NULL == pMode) {
        return E_INVALID_ARGUMENT;
    }

    if ((mode & ACC_WRITE) && (mode & ACC_READ)) {
        *pMode = FileAccessMode_ReadWrite;
    }
    else if ((mode & ACC_WRITE) && !(mode & ACC_READ)) {
        *pMode = FileAccessMode_Write;
    }
    else if (mode & ACC_READ) {
        *pMode = FileAccessMode_Read;
    }
    else {
        if (pFile->f_node == pFile->pFatDir->p_Sharedata->procroot) {
            *pMode = FileAccessMode_ReadWrite;
        }
        else {
            DPRINTF(("Unknown file mode = 0x%08x!\n", mode));
            return E_INVALID_ARGUMENT;
        }
    }
    return NOERROR;
}

ECode FSetAccessMode(
    class CFatFile *pFile,
    /* [in] */ FileAccessMode mode)
{
    int readonly = 0;

    if (pFile->f_node->n_sharemode != FileShareMode_Write &&
        (UInt32)pFile != pFile->f_node->n_firsthandle) {
        return E_FS_FILE_BUSY;
    }

    switch (mode) {
        case FileAccessMode_Read:
            readonly = 1;
            break;

        case FileAccessMode_Write:

        case FileAccessMode_ReadWrite:
            break;

        default:
            DPRINTF(("Unknown file mode = 0x%08x!\n", mode));
            return E_INVALID_ARGUMENT;
    }

    pFile->pFatDir->dir_readonly(pFile, readonly);
    pFile->pFatDir->sync();
    return NOERROR;
}

ECode FGetShareMode(
    class CFatFile *pFile,
    /* [out] */ FileShareMode * pMode)
{
    if (NULL == pMode)
        return E_INVALID_ARGUMENT;

    switch (pFile->f_node->n_sharemode) {
        case FileShareMode_Deny:
            *pMode = FileShareMode_Deny;
            break;
        case FileShareMode_Read:
            *pMode = FileShareMode_Read;
            break;
        case FileShareMode_Write:
            *pMode = FileShareMode_Write;
            break;
    }
    return NOERROR;
}

ECode FSetShareMode(
    class CFatFile *pFile,
    /* [in] */ FileShareMode mode)
{
    ECode ec = NOERROR;

    switch (mode) {
        case FileShareMode_Write:
            if (pFile->f_node->n_sharemode == FileShareMode_Write)
                return NOERROR;
            if (pFile->f_node->n_firsthandle != (UInt32) pFile &&
                pFile->f_node->n_firsthandle != 0)
                return E_FS_FILE_BUSY;
            pFile->f_node->n_firsthandle = 0;
            pFile->f_node->n_sharemode = FileShareMode_Write;
            break;
        case FileShareMode_Read:
            if (pFile->f_node->n_firsthandle != (UInt32) pFile &&
                pFile->f_node->n_firsthandle != 0)
                return E_FS_FILE_BUSY;
            if (pFile->f_node->n_refs != 1)
                return E_FS_FILE_BUSY;
            pFile->f_node->n_firsthandle = (UInt32)pFile;
            pFile->f_node->n_sharemode = FileShareMode_Read;

            break;
        case FileShareMode_Deny:
            if (pFile->f_node->n_firsthandle != (UInt32) pFile &&
                pFile->f_node->n_firsthandle != 0)
                return E_FS_FILE_BUSY;
            if (pFile->f_node->n_refs != 1)
                return E_FS_FILE_BUSY;

            pFile->f_node->n_firsthandle = (UInt32)pFile;
            pFile->f_node->n_sharemode = FileShareMode_Deny;

            break;
        default:
            return E_INVALID_ARGUMENT;
            break;
    }

    return ec;
}

ECode FSetStat(
    class CFatFile *pFile,
    /* [in] */ UInt32 mode,
    /* [in] */ UInt32 atime,
    /* [in] */ UInt32 mtime,
    /* [in] */ UInt32 ctime,
    /* [in] */ UInt32 size,
    /* [in] */ UInt64 blocks,
    /* [in] */ UInt32 blksize)
{
    FILESTAT Stat;
    UInt32 grfStatFlag = 1;

    if (!(pFile->f_node->n_mode & ACC_WRITE)) {
        return ErrorToECODE(EPERM, "FSetStat");
    }

    if (pFile->f_node->n_sharemode != FileShareMode_Write &&
        (UInt32)pFile != pFile->f_node->n_firsthandle) {
        return E_FS_FILE_BUSY;
    }

    Stat.mode = mode;
    Stat.atime = atime;
    Stat.mtime = mtime;
    Stat.ctime = ctime;
    Stat.size = size;
    Stat.blocks = blocks;
    Stat.blksize = blksize;
    pFile->pFatDir->fatfs_stat(pFile, &Stat, &grfStatFlag);

    return NOERROR;
}

ECode FGetStat(
    class CFatFile *pFile,
    /* [out] */ UInt32 * pMode,
    /* [out] */ UInt32 * pAtime,
    /* [out] */ UInt32 * pMtime,
    /* [out] */ UInt32 * pCtime,
    /* [out] */ UInt32 * pSize,
    /* [out] */ UInt64 * pBlocks,
    /* [out] */ UInt32 * pBlksize)
{
    FILESTAT Stat;
    UInt32 grfStatFlag = 0;

    if (NULL == pMode || NULL == pAtime || NULL == pMtime || NULL == pCtime \
        || NULL == pSize || NULL == pBlocks || NULL == pBlksize) {
        return E_INVALID_ARGUMENT;
    }

    pFile->pFatDir->fatfs_stat(pFile, &Stat, &grfStatFlag);
    *pMode = Stat.mode;
    *pAtime = Stat.atime;
    *pMtime = Stat.mtime;
    *pCtime = Stat.ctime;
    *pSize = Stat.size;
    *pBlocks = Stat.blocks;
    *pBlksize = Stat.blksize;
    return NOERROR;
}
